﻿using OA.Infrastructure.Extensions;

namespace AMS.Services.Impl;

public class FixedAssetsConsumingService : IFixedAssetsConsumingService
{
    private readonly DbContext _context;

    private readonly IApprovalService _approvalService;

    public FixedAssetsConsumingService(DbContext context, IApprovalService approvalService)
    {
        _context = context;

        _approvalService = approvalService;
    }

    public async Task<PageResult<FixedAssetsConsuming.QueryModel>> GetPageAsync(FixedAssetsConsuming.ConditionSelectorCondition condition, CancellationToken cancellationToken)
    {
        var result = new PageResult<FixedAssetsConsuming.QueryModel>();

        var currentUser = _context.CurrentUser();

        try
        {
            var expression = GetConditionSelectorCondition(condition);

            var data = await _context.Set<FixedAssetsConsuming>()
                .Include(x => x.Assets).ThenInclude(x => x.Category)
                .Include(x => x.Assets).ThenInclude(x => x.Supplier)
                .Include(x => x.Consumer)
                .Where(expression)
                .OrderByDescending(x => x.CreatedDate)
                .Skip((condition.PageNum - 1) * condition.PageSize)
                .Take(condition.PageSize)
                .AsNoTracking()
                .ToListAsync(cancellationToken);

            result.Data = data.Select(x => GetQueryModel(x));

            result.PageNum = condition.PageNum;

            result.PageSize = condition.PageSize;

            result.Total = await _context.Set<FixedAssetsConsuming>()/*.Where(accessExpression)*/.CountAsync(expression, cancellationToken);

            result.Status = OperationalResult.SUCCESS;
        }
        catch (Exception error)
        {
            result.Status = OperationalResult.ERROR;

            result.Message = error.ToString();
        }

        return result;
    }

    public async Task<OperationalResult> CreateAsync(FixedAssetsConsuming.CreateModel fixedAssetsConsuming, CancellationToken cancellationToken)
    {
        var result = new OperationalResult();

        var currentUser = _context.CurrentUser();

        try
        {
            var entity = new FixedAssetsConsuming
            {
                UseLocation = fixedAssetsConsuming.UseLocation,
                Remark = fixedAssetsConsuming.Remark,
            };

            var assets = await _context.Set<FixedAssets>().FindAsync(new object[] { fixedAssetsConsuming.AssetsId }, cancellationToken);

            if (assets is not null)
            {
                entity.Assets = assets;
                entity.Consumer = currentUser;
                entity.Returning = null;
            }

            await _context.Set<FixedAssetsConsuming>().AddAsync(entity, cancellationToken);

            var affectedRows = await _context.SaveChangesAsync(cancellationToken);

            result.Status = affectedRows > 0 ? OperationalResult.SUCCESS : OperationalResult.FAILURE;

            if (result.Status == OperationalResult.SUCCESS)
            {
                _ = await _approvalService.CreateApprovalAsync(currentUser, entity.Id, ApprovalType.Consuming, AssetsType.Durable, fixedAssetsConsuming.Remark, ApprovalService.fixedAssetsConsumingApprovalFlow, cancellationToken);
            }

            result.Message = $"{affectedRows} rows affected.";
        }
        catch (Exception error)
        {
            result.Status = OperationalResult.ERROR;

            result.Message = error.ToString();
        }

        return result;
    }

    public async Task<OperationalResult> ReturnAsync(int id, CancellationToken cancellationToken)
    {
        var result = new OperationalResult();

        var currentUser = _context.CurrentUser();

        try
        {
            var assetsConsumings = await _context.Set<FixedAssetsConsuming>().Include(n => n.Assets).FirstOrDefaultAsync(x => x.Id == id, cancellationToken);

            if (assetsConsumings is null)
            {
                result.Status = OperationalResult.FAILURE;

                return result;
            }

            var existsPendingApproval = await _context.Set<Approval>().Include(n => n.Committer).AnyAsync(m =>
                m.AssetsType == AssetsType.Durable &&
                m.Status == ApprovalStatus.Pending &&
                m.Type == ApprovalType.Return &&
                m.Committer.Id == currentUser.Id &&
                m.FormId == assetsConsumings.Id, cancellationToken);

            if (existsPendingApproval && assetsConsumings.Returning is not null && assetsConsumings.Returning.Value)
            {
                result.Status = OperationalResult.FAILURE;
                result.Message = "请不要重复提交归还申请";

                return result;
            }

            assetsConsumings.Returning = true;

            _ = await _approvalService.CreateApprovalAsync(currentUser, assetsConsumings.Id, ApprovalType.Return, AssetsType.Durable, null, ApprovalService.fixedAssetsReturnApprovalFlow, cancellationToken);

            _context.Set<FixedAssetsConsuming>().Update(assetsConsumings);
            await _context.SaveChangesAsync(cancellationToken);

            result.Message = $"已提交归还申请";
        }
        catch (Exception error)
        {
            result.Status = OperationalResult.ERROR;

            result.Message = error.ToString();
        }

        return result;
    }


    private static FixedAssetsConsuming.QueryModel GetQueryModel(FixedAssetsConsuming fixedAssetsConsuming)
    {
        return new FixedAssetsConsuming.QueryModel
        {
            Id = fixedAssetsConsuming.Id,
            CreatedDate = fixedAssetsConsuming.CreatedDate,
            LastModifiedDate = fixedAssetsConsuming.LastModifiedDate,
            Assets = FixedAssetsService.GetQueryModel(fixedAssetsConsuming.Assets),
            UseLocation = fixedAssetsConsuming.UseLocation,
            Remark = fixedAssetsConsuming.Remark,
            Consumer = DefinitionServiceExtensions.GetUserQueryModel(fixedAssetsConsuming.Consumer),
            Returning = fixedAssetsConsuming.Returning,
            Status = fixedAssetsConsuming.Status,
        };
    }

    private static Expression<Func<FixedAssetsConsuming, bool>> GetConditionSelectorCondition(FixedAssetsConsuming.ConditionSelectorCondition condition)
    {
        Expression<Func<FixedAssetsConsuming, bool>>? expression = null;

        if (condition.Keywords is not null)
        {
            Expression<Func<FixedAssetsConsuming, bool>>? keywords = null;
            keywords = fixedAssetsConsuming => fixedAssetsConsuming.Assets.Name.Contains(condition.Keywords);
            keywords = keywords.Or(fixedAssetsConsuming => fixedAssetsConsuming.Assets.Specs.Contains(condition.Keywords));
            keywords = keywords.Or(fixedAssetsConsuming => fixedAssetsConsuming.Assets.Supplier.Name.Contains(condition.Keywords));
            keywords = keywords.Or(fixedAssetsConsuming => fixedAssetsConsuming.UseLocation.Contains(condition.Keywords));
            expression = expression?.And(keywords) ?? keywords;
        }

        if (condition.StatusSelector is not null)
        {
            Expression<Func<FixedAssetsConsuming, bool>>? statusSelector = null;
            statusSelector = fixedAssetsConsuming => condition.StatusSelector == fixedAssetsConsuming.Status;
            expression = expression?.And(statusSelector) ?? statusSelector;
        }

        return expression ?? (fixedAssetsConsuming => true);
    }
}